<template>
  <div ref="list" style="margin-left: 0px; width: 100%">
    <div
      v-for="(item, index) in children"
      :key="index"
      :style="{
        height: `${rowHeight}px`,
      }"
      style="
        width: 100%;
        padding: 4px 0;
        box-sizing: content-box;
        line-height: 1;
      "
      class="list_item"
      @click.self="setItemSelect(item, !item.checked, filterText)"
    >
      <i
        :class="[
          item.caretFlag ? 'el-icon-caret-bottom' : 'el-icon-caret-right',
        ]"
        :style="{
          visibility:
            item[keys.childKey] && item[keys.childKey].length
              ? 'visible'
              : 'hidden',
          marginLeft: 10 * item.level + 'px',
        }"
        style="float: left"
        @click="setArrow(item)"
      />
      <el-checkbox
        :key="JSON.stringify(item[keys.valueKey])"
        style="{marginLeft: '6px',heigt: rowHeight+'px'}"
        :indeterminate="!item.checked && item.indeterminate"
        v-model="item.checked"
        @change="
          (status) => {
            selectItem(item, status);
          }
        "
      />
      <span
        style="margin-left: 6px"
        v-html="getFilterHightLight(item[keys.textKey])"
      ></span>
    </div>
  </div>
</template>

<script>
export default {
  name: "NodeContent",
  components: {},
  props: {
    children: {
      type: Array,
    },
    rowHeight: {
      type: Number,
      default: 16,
    },
    offsetTop: {
      type: Number,
      require: true,
    },
    // 是否是可单选模式-单选模式下（点击节点所在div能选中节点），节点选中不会影响到其他节点选择。非单选模式下，父元素不可单独被选中，只可全选和取消全选，节点选中会同步判断是否选中其父元素（即其父元素的选中、半选依赖于子元素）
    single: {
      type: Boolean,
      default: false,
    },
    //选中节点数组
    selectArr_f: {
      type: Array,
      require: true,
    },
    // 树的筛选条件
    filterText: {
      type: String,
    },
    keys: {
      type: Object,
      require: true,
    },
    // 完整树的备份
    flatArr_f: {
      type: Array,
    },
    // 当前树的所有节点
    flatArr: {
      type: Array,
    },
  },
  data() {
    return {
      marginLeft: this.n + 1,
      childArr: null,
      index: null,
      indexArr: [],
      selectArr: [],
      recordSelectedNode: {}, //记录点击的node
      recordHandlerNodes: [], //记录操作过的节点
    };
  },
  computed: {},
  watch: {
    offsetTop(newVal) {
      this.$refs.list.style.transform = `translateY(${newVal}px)`;
    },
    selectArr_f(newVal, oldVal) {
      this.selectArr = newVal;
    },
  },
  mounted() {},
  methods: {
    // 点击箭头
    setArrow(item) {
      // 修改记录是否点击展开
      this.$set(item, "caretFlag", !item["caretFlag"]);

      // 折叠展开时对子孙后代元素的处理
      if (item.hasOwnProperty(this.keys.childKey)) {
        // 折叠
        if (!item["caretFlag"]) {
          this.indexArr.length = 0;
          //删除后代元素
          this.indexArr.push(this.children.indexOf(item) + 1);
          let childNodes = this.getChildrenNone(item[this.keys.childKey], []);
          item.hideNodes = childNodes;
          this.indexArr.push(childNodes.length);

          this.$emit("setCollapse", this.indexArr, item);
        } else {
          // 展开
          let index = this.children.indexOf(item) + 1;

          this.$emit("setExpand", index, item);
        }
      }
    },
    // 获取后代直接删除元素数组
    getChildrenNone(nodes, arr) {
      nodes.forEach((ele, index) => {
        arr.push(ele);
        // 如果子元素折叠了说明子元素的后代元素已经被删除了，所以要排除这种情况，限定条件ele.caretFlag
        if (ele.hasOwnProperty(this.keys.childKey) && ele.caretFlag) {
          this.getChildrenNone(ele[this.keys.childKey], arr);
        } else {
          return;
        }
      });
      return arr;
    },
    // 记录-完整树操作节点
    recordHandlerNodesFun(recordArr, nodes) {
      nodes.forEach((node) => {
        let index = recordArr.indexOf(node);
        if (index > -1) {
          recordArr.splice(index, 1, node);
        } else {
          recordArr.splice(recordArr.length, 1, node);
        }
      });
    },
    // 清空记录完整树操作节点
    clearRecord() {
      this.recordHandlerNodes.splice(0, this.recordHandlerNodes.length);
    },
    //复选框事件-针对筛选树和完整树上的操作
    selectItem(item, status) {
      // 记录所有操作节点
      let childArr = [];
      childArr.push(item);
      if (item.hasOwnProperty(this.keys.childKey)) {
        this.getItemChildren(item, childArr);
      }
      if (this.filterText) {
        // 让筛选树操作节点同步完整树
        let childArr_f = [];
        childArr.forEach((ele) => {
          this.recordSelectedNode = this.getTreeNode(ele, this.flatArr_f);
          this.innerSetItemSelect(this.recordSelectedNode, status, false);
          childArr_f.push(this.recordSelectedNode);
        });
        this.recordHandlerNodesFun(this.recordHandlerNodes, childArr_f);
      } else {
        this.recordHandlerNodesFun(this.recordHandlerNodes, childArr);
      }
      this.innerSelectItem(item, status);
    },
    innerSelectItem(item, status, flag = true) {
      item.checked = status;
      if (flag) {
        this.operateSelectArr(status ? 0 : 1, [item]);
      }

      // 有后代
      if (item.hasOwnProperty(this.keys.childKey)) {
        // 1.全选或取消全选时清除半选态
        // this.$set(item, "indeterminate", false);
        let nodesArr = this.recursionFunc(
          item[this.keys.childKey],
          { indeterminate: status ? true : false, checked: status },
          this.keys.childKey,
          []
        );
        if (flag) {
          this.operateSelectArr(1, nodesArr); // 全选去重及全部取消
        }

        //全选
        if (status) {
          item.indeterminate = true;
          if (flag) {
            this.operateSelectArr(0, nodesArr);
          }
        } else {
          //取消全选
          item.indeterminate = false;
        }
      }
      // 有祖宗
      if (item.hasOwnProperty("mother")) {
        if (this.single) {
          this.changeSelect_single(item.mother, "mother");
        } else {
          this.changeSelect(item.mother, "mother");
        }
      }
      this.recordSelectedNode = item;
      this.$emit(
        "change",
        item,
        status,
        this.selectArr,
        this.recordHandlerNodes
      );
    },

    // 删除新增选中节点数组operate:0-增加，1-删除
    operateSelectArr(operate, nodes) {
      if (operate === 0) {
        this.selectArr.splice(this.selectArr.length, operate, ...nodes);
      } else if (operate === 1) {
        nodes.forEach((ele) => {
          let nodeIndex = -1;
          this.selectArr.forEach((item, index) => {
            if (item[this.keys.valueKey] === ele[this.keys.valueKey]) {
              nodeIndex = index;
              return;
            }
          });

          if (nodeIndex > -1) {
            this.selectArr.splice(nodeIndex, operate);
          }
        });
      }
    },
    //子元素选中后母元素-半选、全选、取消全选--------------------父元素可单选模式
    changeSelect_single(item, judgesAttribute) {
      let childArr = [];
      this.getItemChildren(item, childArr);
      let selectedNum = childArr.filter((ele) => ele.checked).length;
      if (selectedNum === 0) {
        //取消半选
        item.indeterminate = false;
      } else if (selectedNum <= childArr.length && selectedNum > 0) {
        //如果父级最近一个已经半选，则祖宗都无需再重复设置
        if (!item.indeterminate) {
          //半选
          item.indeterminate = true;
        } else {
          return;
        }
      }
      if (item.hasOwnProperty(judgesAttribute)) {
        this.changeSelect_single(item[judgesAttribute], judgesAttribute);
      }
    },

    //子元素选中后母元素-半选、全选、取消全选-------------父元素不可单选模式
    changeSelect(item, judgesAttribute) {
      let attributeObj = {};
      let childArr = [];
      this.getItemChildren(item, childArr);
      let selectedNum = childArr.filter((ele) => ele.checked).length;
      if (selectedNum === childArr.length) {
        //全选
        item.checked = true;
        item.indeterminate = false;
        this.operateSelectArr(0, [item]);
      } else if (selectedNum === 0) {
        if (item.checked) {
          this.operateSelectArr(1, [item]);
        }
        //取消全选
        item.checked = false;
        item.indeterminate = false;
      } else {
        if (!item.indeterminate) {
          //如果父级最近一个已经半选，则祖宗都无需再重复设置
          if (item.checked) {
            this.operateSelectArr(1, [item]);
          }
          //半选
          item.checked = false;
          item.indeterminate = true;
        } else {
          return;
        }
      }
      Object.keys(attributeObj).forEach((keys) => {
        this.$set(item, keys, attributeObj[keys]);
      });
      if (item.hasOwnProperty(judgesAttribute)) {
        this.changeSelect(item[judgesAttribute], judgesAttribute);
      }
    },

    // 计算当前元素有多少后代元素
    getItemChildren(item, arr) {
      item[this.keys.childKey].forEach((ele) => {
        arr.push(ele);
        if (ele.hasOwnProperty(this.keys.childKey)) {
          this.getItemChildren(ele, arr);
        }
      });
      return arr;
    },
    // 递归children-操作元素，attributeObj-要设置的属性对象，judgesAttribute-判断是否有什么元素
    recursionFunc(children, attributeObj, judgesAttribute, arr) {
      children.forEach((item) => {
        arr.push(item);
        Object.keys(attributeObj).forEach((keys) => {
          if (keys != "indeterminate") {
            this.$set(item, keys, attributeObj[keys]);
          }
        });
        if (item.hasOwnProperty(judgesAttribute)) {
          this.$set(item, "indeterminate", attributeObj["indeterminate"]);
          this.recursionFunc(
            item[judgesAttribute],
            attributeObj,
            judgesAttribute,
            arr
          );
        }
      });
      return arr;
    },
    // 单选-针对筛选树和完整树上的操作
    setItemSelect(item, checked, filterText, flag = true) {
      // 让筛选树操作节点同步完整树
      if (filterText) {
        //获取完整树上与点击的筛选树节点key值一致的节点
        this.recordSelectedNode = this.getTreeNode(item, this.flatArr_f);
        // 设置完整树同步点击该节点-仅展示层面
        this.innerSetItemSelect(this.recordSelectedNode, checked, false);
        // 记录-完整树操作节点
        this.recordHandlerNodesFun(this.recordHandlerNodes, [
          this.recordSelectedNode,
        ]);
      } else {
        // 记录-让完整树操作节点同步筛选树(因为筛选树是筛选时随机生成的，所以此处只能记录而不能同步操作)
        this.recordHandlerNodesFun(this.recordHandlerNodes, [item]);
      }
      this.innerSetItemSelect(item, checked, flag);
    },
    // 单选-针对直接由选中项来操作树的选中及非选中(选中的节点里面既可能有完整树上的节点，也可能有筛选树上的节点，为了性能没有做节点的同步成一棵树上的操作)
    setItemSelectBySelected(node) {
      let treeNode = null;
      let filterNode = null;
      if (this.filterText) {
        // 筛选树情况下要同步完整树的点选
        treeNode = this.getTreeNode(node, this.flatArr_f);
        // 完整树展示层面的节点取消
        this.innerSetItemSelect(treeNode, false, false);
      }
      // 无论筛选树还是完整树情况下都需要操作当前展示节点
      filterNode = this.getTreeNode(node, this.flatArr);
      // 当前展示层面的节点取消
      if (filterNode) {
        this.innerSetItemSelect(filterNode, false, false);
      }
      // 选中节点取消
      this.innerSetItemSelect(node, false);
      if (this.filterText) {
        //筛选情况下的完整树操作节点记录节点
        this.recordSelectedNode = treeNode;
      } else {
        //非筛选情况下的完整树操作节点记录节点
        this.recordSelectedNode = filterNode;
      }
      // 记录-完整树操作节点
      this.recordHandlerNodesFun(this.recordHandlerNodes, [
        this.recordSelectedNode,
      ]);
    },
    // 单选
    innerSetItemSelect(item, checked, flag = true) {
      if (!this.single) {
        return;
      }
      item.checked = checked;
      // 有祖宗
      if (item.hasOwnProperty("mother")) {
        this.changeSelect_single(item.mother, "mother");
      }
      if (flag) {
        if (item.checked) {
          this.operateSelectArr(0, [item]);
        } else {
          this.operateSelectArr(1, [item]);
        }
      }
      this.recordSelectedNode = item;
      this.$emit(
        "nodeClick",
        item,
        item.checked,
        this.selectArr,
        this.recordHandlerNodes
      );
    },
    // 添加关键字高亮
    getFilterHightLight(label) {
      return label.replace(
        this.filterText,
        `<span style="background: #ffff00;">${this.filterText}</span>`
      );
    },
    // 获取筛选树及完整树上相同的节点
    getTreeNode(node, nodes) {
      for (let i = 0; i < nodes.length; i++) {
        const item = nodes[i];
        if (item[this.keys.valueKey] == node[this.keys.valueKey]) {
          return item;
        }
        if (item.hasOwnProperty(this.keys.childKey)) {
          let sameNode = this.getTreeNode(node, item[this.keys.childKey]);
          if (sameNode) {
            return sameNode;
          }
        }
      }
    },
  },
};
</script>

<style>
.list_item:hover {
  background-color: #e9edf2;
}
</style>
